﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using XDL.Framework.Messaging;
using XDL.Framework.Particles;
using XDL.Framework;
using JumpNFight.Weapons;

namespace JumpNFight.Simulation
{
    abstract class EntityBase :
        GameComponent,
        IEventDispatcherClient
    {
        /// <summary>
        /// Random generator
        /// </summary>
        protected static readonly Random Rnd = new Random();

        public const int DieEvent = 1001;
        public const int HitEvent = 1002;

        /// <summary>
        /// Hit points
        /// </summary>
        public int Health = 100;

        /// <summary>
        /// Records the last hit time
        /// </summary>
        /// <remarks>
        /// Total time since game start
        /// </remarks>
        public TimeSpan LastHitTime;

        /// <summary>
        /// Records death time
        /// </summary>
        public TimeSpan DeathTime;

        /// <summary>
        /// Indicates if the actor can interact with the world.
        /// </summary>
        public bool IsAlive = true;

        /// <summary>
        /// Indicates whether the actor can be removed from the list of active entities.
        /// </summary>
        public bool IsActive = true;

        /// <summary>
        /// Helper boolean to record damage type
        /// </summary>
        protected bool IsFireDamage
        {
            get;
            set;
        }

        ParticleEmitter m_trailEmitter;
        
        /// <summary>
        /// Returns whether the badguy has been cut in half!
        /// </summary>
        public bool IsSliced
        {
            get
            {
                return Sliceable.IsSliced;
            }
                
        }

        /// <summary>
        /// Indicates whether the entity is running left or riht.
        /// </summary>
        public bool IsRunningRight
        {
            get;
            set;
        }

        /// <summary>
        /// Physic body
        /// </summary>
        public abstract Moveable Moveable
        {
            get;
        }

        /// <summary>
        /// Secondary physic body (in case, say, head is separated from body ;)
        /// </summary>
        public abstract Sliceable Sliceable
        {
            get;
        }

        /// <summary>
        /// World the actor is in.
        /// </summary>
        protected World World
        {
            get;
            private set;
        }

        int m_id;
        public int ID
        {
            get
            {
                return m_id;
            }
        }

        public EntityBase(int id, Game game):
            base(game)
        {
            m_id = id;
            this.World = GameServices.GetService<World>();            

            // register the object in the event system
            GameServices.EventDispatcher.RegisterClient(this);

            ParticleSystem ps = GameServices.GetService<ParticleSystem>("Particles/SmallSmoke");
            m_trailEmitter = new ParticleEmitter(ps, Rnd.Next(30, 40));
        }

        public override void Update(GameTime gameTime)
        {
            // going balistic!!
            if (!IsAlive)
            {
                float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
                Moveable.UpdateVelocity(dt);
                Moveable.UpdatePosition(dt);
                // update slice (if any)    
                Sliceable.Update(gameTime);

                // did we get hit by a "fire" weapon?
                if ( IsFireDamage )
                    m_trailEmitter.Update(gameTime,new Vector3(Moveable.X, Moveable.Y, 0), Vector3.Zero);

                if (this.World.IsOutside(new Rectangle((int)Moveable.X - Moveable.Width / 2, (int)Moveable.Y - Moveable.Width / 2, Moveable.Width, Moveable.Height)))
                {
                    IsActive = false;
                    // unregister the badguy from the event queue
                    GameServices.EventDispatcher.UnregisterClient(this);
                }
            }
        }

        public virtual bool ProcessEvent(GameTime gameTime, int msgId, EventArgs e)
        {
            switch (msgId)
            {
                case EntityBase.DieEvent:
                    LastHitTime = gameTime.TotalGameTime;
                    Kill(gameTime, Vector2.Zero);
                    break;

                case DiscGunWeaponSystem.HitEvent:
                    {
                        DiscHitEventArgs hitArgs = (DiscHitEventArgs)e;
                        LastHitTime = gameTime.TotalGameTime;
                        Health -= hitArgs.Weapon.Damage;
                        if (Health <= 0)
                        {
                            // get relative height
                            int h = (int)((hitArgs.TargetPosition.Y + Moveable.Height / 2) - hitArgs.DiscPosition.Y);
                            System.Diagnostics.Debug.Assert(h >= 0);
                            GameServices.EventDispatcher.PostEvent(hitArgs.OwnerID, Player.RegisterKillEvent, null);
                            Kill(gameTime, Vector2.Zero);
                            Sliceable.Slice(h);
                        }
                    }
                    break;
            }

            return true;
        }

        /// <summary>
        /// Helper to kill this entity.
        /// </summary>
        protected virtual void Kill(GameTime gameTime, Vector2 velocity)
        {
            DeathTime = gameTime.TotalGameTime;
            IsAlive = false;
            Moveable.TouchingGround = false;
            Moveable.Drag = 0;
            // default velocity or inherited from weapon hit
            if (velocity.LengthSquared() != 0)
            {
                Moveable.Velocity = velocity;
            }
            else
            {
                Moveable.Velocity = new Vector2(Rnd.Next(-32, 32), -Rnd.Next(400, 450));
            }
        }
    }
}
